package vn.appsmobi.model;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import vn.appsmobi.ulti.Constant;
import vn.appsmobi.ulti.DownloadInstallManager;
import vn.appsmobi.ulti.LocalAppManager;
import vn.appsmobi.ulti.Log;
import android.content.Context;
import android.text.TextUtils;
import android.view.View;

public class AppInfo {

	private String id;
	private String name;
	private String iconLink;
	private int rateNum;
	private int price;
	private String agencyid;
	private String agencyName;
	private String description;
	private String detail;
	private String linkApk;
	private String linkHome;
	private int downloadNum;
	private long downloadId;
	private float size;
	private Calendar uploadDate;
	private ArrayList<AppInfo> relativeApps;
	private ArrayList<String> images;
	private Boolean isPay;
	
	/**
	 * @param agencyid the agencyid to set
	 */
	public void setAgencyid(String agencyid) {
		this.agencyid = agencyid;
	}


	private Boolean isOnline;
	private Boolean isHot;
	private Boolean isHome;	
	private String packageName;
	private String version;
	private int versionCode;
	private String hash;
	private int minSDK;
	private String category;
	private String source;
	public AppStatus status = AppStatus.STATUS_NORMAL;
	private static AppInfoCache sAppInfoCache;
	private CopyOnWriteArraySet<AppInfoUpdateListener> mBaseListeners;
	

	public AppInfo() {
		// TODO Auto-generated constructor stub
	}
	
	public static void init(Context paramContext)
	{
	    if (sAppInfoCache == null)
	      sAppInfoCache = new AppInfoCache(paramContext);
	}
	
	public AppInfo(String id, String name, String iconLink, int rateNum, int price,
			String agencyName, String description, String linkApk,
			int downloadNum, float size, Calendar uploadDate,
			ArrayList<AppInfo> relativeApps, ArrayList<String> images) {
		super();
		this.id = id;
		this.name = name;
		this.iconLink = iconLink;
		this.rateNum = rateNum;
		this.price = price;
		this.agencyName = agencyName;
		this.description = description;
		this.linkApk = linkApk;
		this.downloadNum = downloadNum;
		this.size = size;
		this.uploadDate = uploadDate;
		this.relativeApps = relativeApps;
		this.images = images;
	}

	
	public static AppInfo get(JSONObject json)
	{		
		return AppInfo.cacheOrUpdate(initFromJson(json));
	}

	public static AppInfo initFromJson(JSONObject json) {
		AppInfo appinfo = new AppInfo();
		try {
			appinfo.id = json.getString(Constant.id);
			appinfo.name = json.getString(Constant.app_name);
			appinfo.iconLink = json.getString(Constant.iconLink);
			appinfo.rateNum = json.getInt(Constant.rateNum);
			appinfo.price = json.getInt(Constant.is_pay);
			
			appinfo.agencyName = json.getString(Constant.company);
			
			appinfo.linkApk = json.getString(Constant.linkApk);
			appinfo.downloadNum = json.getInt(Constant.downloaded);
			appinfo.size = Float.parseFloat(json.getString(Constant.file_size));
			appinfo.isHot = json.getInt(Constant.is_hot) == 1 ? true : false;
			
			appinfo.isPay = json.getInt(Constant.is_pay) == 1 ? true : false;
			appinfo.isOnline = json.getInt(Constant.is_online) == 1 ? true : false;
			
		
			if (json.has(Constant.homeicon)) {
				appinfo.linkHome = json.getString(Constant.homeicon);
			}
			
			if (json.has(Constant.is_home)) {
				appinfo.isHome = json.getInt(Constant.is_home) == 1 ? true : false;
			}
			
			
			if (json.has(Constant.category)) {
				appinfo.category = json.getString(Constant.category);
			}
			if (json.has(Constant.agency_id)) {
				appinfo.agencyid = json.getString(Constant.agency_id);
			}
			if (json.has(Constant.package_name)) {
				appinfo.packageName = json.getString(Constant.package_name);
			}
			if (json.has(Constant.version)) {
				appinfo.version = json.getString(Constant.version);
				if(appinfo.version.equals(""))
					appinfo.version = "0";
			}
			
			if (json.has(Constant.versionCode)) {
				appinfo.versionCode = json.getInt(Constant.versionCode);
			}
			
			if (json.has(Constant.hash)) {
				appinfo.hash = json.getString(Constant.hash);
			}
			
			if (json.has(Constant.minsdk)) {
				appinfo.minSDK = json.getInt(Constant.minsdk);
			}
			
			
			if (json.has(Constant.description)) {
				appinfo.description = json.getString(Constant.description);
			}
			if (json.has(Constant.detail)) {
				appinfo.detail = json.getString(Constant.detail);
			}
			if (json.has(Constant.listOfImage)) {
				JSONArray arrayImages = json.getJSONArray(Constant.listOfImage);
				ArrayList<String> images = new ArrayList<String>();
				for (int i = 0; i < arrayImages.length(); i++) {
					images.add(arrayImages.getString(i));
				}
				appinfo.images = images;
			}
			if (json.has(Constant.listOfRelativeApp)) {
				JSONArray arrayApps = json
						.getJSONArray(Constant.listOfRelativeApp);
				ArrayList<AppInfo> relativeApps = new ArrayList<AppInfo>();
				for (int i = 0; i < arrayApps.length(); i++) {
					relativeApps.add(AppInfo.get(arrayApps.getJSONObject(i)));
				}
				appinfo.relativeApps = relativeApps;
				appinfo.updateAppStatus();
			}
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return appinfo;
	}

	@Override
	public String toString() {
		return "App [id=" + id + ", name=" + name + ", iconLink=" + iconLink
				+ ", rateNum=" + rateNum + ", price=" + price
				+ ", companyName=" + agencyName + ", description="
				+ description + ", linkApk=" + linkApk + ", downloadNum="
				+ downloadNum + ", size=" + size + ", uploadDate=" + uploadDate
				+ ", relativeApps=" + relativeApps + ", images=" + images + "]";
	}
	
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getIconLink() {
		return iconLink;
	}

	public void setIconLink(String iconLink) {
		this.iconLink = iconLink;
	}

	public int getRateNum() {
		return rateNum;
	}

	public void setRateNum(int rateNum) {
		this.rateNum = rateNum;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public String getAgencyId() {
		return agencyid;
	}
	
	public String getCompanyName() {
		return agencyName;
	}

	public void setCompanyName(String companyName) {
		this.agencyName = companyName;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getLinkApk() {
		return linkApk;
	}
	
	public String getLinkHome() {
		return linkHome;
	}

	public void setLinkApk(String linkApk) {
		this.linkApk = linkApk;
	}

	public int getDownloadNum() {
		return downloadNum;
	}

	public void setDownloadNum(int downloadNum) {
		this.downloadNum = downloadNum;
	}

	public float getSize() {
		return size;
	}

	public void setSize(float size) {
		this.size = size;
	}

	public Calendar getUploadDate() {
		return uploadDate;
	}

	public void setUploadDate(Calendar uploadDate) {
		this.uploadDate = uploadDate;
	}

	public ArrayList<AppInfo> getRelativeApps() {
		return relativeApps;
	}

	public void setRelativeApps(ArrayList<AppInfo> relativeApps) {
		this.relativeApps = relativeApps;
	}

	public ArrayList<String> getImages() {
		return images;
	}

	public void setImages(ArrayList<String> images) {
		this.images = images;
	}

	public Boolean getIsPay() {
		return isPay;
	}

	public void setIsPay(Boolean isPay) {
		this.isPay = isPay;
	}

	public Boolean getIsOnline() {
		return isOnline;
	}

	public String getSource() {
		return source;
	}

	public void setSource(String source) {
		this.source = source;
	}

	public void setIsOnline(Boolean isOnline) {
		this.isOnline = isOnline;
	}

	public Boolean getIsHot() {
		return isHot;
	}

	public void setIsHot(Boolean isHot) {
		this.isHot = isHot;
	}

	public String getPackageName() {
		return packageName;
	}
	public String getCategory() {
		return category;
	}
	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}

	public String getVersion() {
		return version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	public int getVersionCode() {
		return versionCode;
	}

	public void setVersionCode(int versionCode) {
		this.versionCode = versionCode;
	}

	public String getHash() {
		return hash;
	}

	public void setHash(String hash) {
		this.hash = hash;
	}

	public int getMinSDK() {
		return minSDK;
	}

	public void setMinSDK(int minSDK) {
		this.minSDK = minSDK;
	}

	public String getDetail() {
		return detail;
	}

	public void setDetail(String detail) {
		this.detail = detail;
	}

	public static AppInfo valueOf(JSONObject json) {
		AppInfo appinfo = AppInfo.get(json);
		Log.v("appinfo agency:" + appinfo.getAgencyId());
		return appinfo;
		
	}
	
	public void removeUpdateListener(AppInfoUpdateListener listener)
	  {
	    if (listener != null)	    
	    {	      
	      try
	      {
	        if (this.mBaseListeners != null)
	          this.mBaseListeners.remove(listener);	        
	      }
	      finally
	      {
	      }
	    }
	  }
	
	public void addUpdateListener(AppInfoUpdateListener listener)
	  {
	    if (listener != null)
	    {
	    	
	    	if (this.mBaseListeners == null)
	            this.mBaseListeners = new CopyOnWriteArraySet<AppInfoUpdateListener>();
	          this.mBaseListeners.add(listener);
	          
	          //Log.v(" this.mBaseListeners size:" +  this.mBaseListeners.size());
	    }	     
	  }
	
	public void updateAppStatus()
	{		
		LocalAppManager localManager = LocalAppManager.getManager();
		AppStatus currentStatus = this.status;
		this.status = AppStatus.STATUS_NORMAL;
	    if (localManager.isInstalled(this.packageName))
	        this.status = AppStatus.STATUS_INSTALLED;	    
	    if(DownloadInstallManager.getManager().isDownloadingOrInstalling(this.id))
	    	this.status = AppStatus.STATUS_INSTALLING;
	    
	    
	  Log.v("update AppStatus:" + currentStatus +"::::" + this.status );

	    //
	    if(currentStatus != this.status)
	    {	       
	    	//Log.v("MarketAppInfo status of app " + this.packageName + " has changed from " + currentStatus + " to " + this.status);
    	   if(this.mBaseListeners != null)
    	   {
    		   Log.v("this.mBaseListeners size: " + this.mBaseListeners .size());
    		   Iterator<AppInfoUpdateListener> iterator = this.mBaseListeners.iterator();
               while (iterator.hasNext())
               {
            	   //Log.v("call status update");
            	   ((AppInfoUpdateListener)iterator.next()).onStatusUpdate(this);
               }               
    	   } 
    	  
	    }
	}
	
	public static AppInfo cacheOrUpdate(AppInfo appinfo)
	{		
	  return sAppInfoCache.cache(appinfo);
	}
	
	public static AppInfo get(String appid)
	{
	   return sAppInfoCache.get(appid);
	}
	
	public static AppInfo getAppByPackage(String packagename)
	{
	   return sAppInfoCache.getAppByPackage(packagename);
	}
	
	
	public boolean canInstallOrUpdate()
	  {
	    AppInfo appinfo = LocalAppManager.getManager().getLocalAppInfo(this.packageName);
	    if (appinfo != null && appinfo.getVersionCode() >  this.getVersionCode())
        {
      	  return true;          
        }
	    return false;
	   	
	  }
	
	public long getDownloadId() {
		return downloadId;
	}

	public void setDownloadId(long downloadId) {
		this.downloadId = downloadId;
	}

	public static abstract interface AppInfoUpdateListener
	  {
	    public abstract void onContentUpdate(AppInfo appInfo);

	    public abstract void onStatusUpdate(AppInfo appInfo);
	  }
	
	 public static enum AppStatus
	  {
		  
		  STATUS_NORMAL("STATUS_INSTALLED",0),STATUS_INSTALLED("STATUS_INSTALLED", 1),STATUS_INSTALLING("STATUS_INSTALLING", 2),STATUS_UPDATE("STATUS_UPDATE",3);
	   
	    AppStatus(String name, int value){}
	    
	    public String toString()
	    {
	      String str;
	      if (this == STATUS_NORMAL)
	        str = "STATUS_NORMAL";
	      else if (this == STATUS_INSTALLED)	        
	          str = "STATUS_INSTAILLED";
	      else if (this == STATUS_INSTALLING)
	          str = "STATUS_INSTALLING";
	      else if (this == STATUS_UPDATE)
	          str = "STATUS_UPDATE";
	      else
	          str = "STATUS_NONE";
	      return str;
	     }
	  }
	 
	
	public static class AppInfoCache
	  {
	    private ConcurrentHashMap<String, AppInfo> mAppInfoCache;
	    private LocalAppManager mAppManager;
	    private ConcurrentHashMap<String, CopyOnWriteArraySet<String>> mPackageToAppIdMap;
	    
	    private DownloadInstallManager.TaskListener mTaskListener = new DownloadInstallManager.TaskListener()
		{
	      public void onTaskFail(String appid, int error)
	      {
	    	  AppInfo appInfo = AppInfo.AppInfoCache.this.get(appid);	    	  	    	
	    	  appInfo.updateAppStatus();
	      }

	      public void onTaskStart(String appid)
	      {
	    	  AppInfo appInfo = AppInfo.AppInfoCache.this.get(appid);	    	  	    	
	    	  appInfo.updateAppStatus();
	      }

	      public void onTaskSuccess(String appid)
	      {	    	  
	    	  AppInfo appInfo = AppInfo.AppInfoCache.this.get(appid);	    	  	    	
	    	  appInfo.updateAppStatus();
	    	  Log.v("onTaskSuccess:" + appInfo.status);
	      }
		};
	  
	    private LocalAppManager.LocalAppInfoUpdateListener mUpdateListener = new LocalAppManager.LocalAppInfoUpdateListener()
	    {
	    	
	    	 private void updateInstalledStatus(AppInfo appinfo)
	         {	   
    	        AppInfo app = AppInfo.get(appinfo.getId());
    	        Log.v("LocalAppInfoUpdateListener updateInstalled Status:" + appinfo.getPackageName());
    	        app.updateAppStatus();	    	      
	         }
			@Override
			public void onContentChanged() {
				Iterator<LocalAppInfo> iterator = AppInfo.AppInfoCache.this.mAppManager.getInstalledApp().iterator();
		        while (iterator.hasNext())
		        {
		        	try{
		        		AppInfo app = AppInfo.getAppByPackage(((iterator.next()).getPackageName()));
		        		if(app != null)
		        			updateInstalledStatus(app);
		        	}catch(Exception e){}
		        	
		        }
		   
			}

			@Override
			public void onContentChanged(AppInfo appInfo) {
				// TODO Auto-generated method stub
				Log.v("LocalAppInfoUpdateListener onContentChanged");
				updateInstalledStatus(appInfo);				
			}

			@Override
			public void onListChanged() {
				// TODO Auto-generated method stub
				Iterator<LocalAppInfo> iterator = AppInfo.AppInfoCache.this.mAppManager.getInstalledApp().iterator();
		        while (iterator.hasNext())
		        {
		        	try{
		        		AppInfo app = AppInfo.getAppByPackage(((iterator.next()).getPackageName()));
		        		if(app != null)
		        			updateInstalledStatus(app);
		        	}catch(Exception e){}
		        }
		        	
				 
			}

			@Override
			public void onListChanged(AppInfo appInfo) {
				updateInstalledStatus(appInfo);
				
			}

			@Override
			public void onLocalInstalledLoaded() {
				
				Iterator<LocalAppInfo> iterator = AppInfo.AppInfoCache.this.mAppManager.getInstalledApp().iterator();
		        while (iterator.hasNext())
		        {		        	
		        	try{
		        		AppInfo app = AppInfo.getAppByPackage(((iterator.next()).getPackageName()));
		        		if(app != null)
		        			updateInstalledStatus(app);
		        	}catch(Exception e){}
		        }
		          
				
			}
	      
	    };
	    public AppInfoCache(Context paramContext)
	    {
	      this.mAppInfoCache = new ConcurrentHashMap<String, AppInfo>();
	      this.mPackageToAppIdMap = new ConcurrentHashMap<String, CopyOnWriteArraySet<String>>();
	      this.mAppManager = LocalAppManager.getManager();
	      this.mAppManager.addUpdateListener(this.mUpdateListener);
	      DownloadInstallManager.getManager().addTaskListener(this.mTaskListener);
	    }

	    private void addToPackageIdMap(AppInfo appinfo)
	    {
	      CopyOnWriteArraySet<String> arraySet = (CopyOnWriteArraySet<String>)this.mPackageToAppIdMap.get(appinfo.packageName);
	      if (arraySet == null)
	      {
	        arraySet = new CopyOnWriteArraySet<String>();	        
	        this.mPackageToAppIdMap.put(appinfo.packageName, arraySet);
	      }
	      if (!arraySet.contains(appinfo.getId()))
	        arraySet.add(appinfo.getId());
	    }

	

	    public AppInfo cache(AppInfo appinfo)
	    {
	    	
	      if (appinfo != null)
	      {
	    	addToPackageIdMap(appinfo); 
	        if (!this.mAppInfoCache.containsKey(appinfo.getId()))
	        {
	          Log.v("AppInfo !containsKey");
	          this.mAppInfoCache.put(appinfo.getId(), appinfo);
	          appinfo.updateAppStatus();
	        }
	        else
	        {
	          AppInfo localApp = (AppInfo)this.mAppInfoCache.get(appinfo.getId());
	          if(appinfo.getDescription() != null)
	          {
	        	  Log.v("AppInfo cache:"+ appinfo.getAgencyId());
	        	  localApp.setDescription(appinfo.getDescription());
	        	  localApp.setDetail(appinfo.getDetail());
	        	  localApp.setDownloadNum(appinfo.getDownloadNum());
	        	  localApp.setImages(appinfo.getImages());
	        	  localApp.setVersion(appinfo.getVersion());
	        	  localApp.setVersionCode(appinfo.getVersionCode());
	        	  localApp.setMinSDK(appinfo.getMinSDK());
	        	  localApp.setHash(appinfo.getHash());
	        	  localApp.setUploadDate(appinfo.getUploadDate());
	        	  if(localApp.getAgencyId() != null)
	        		  localApp.setAgencyid(appinfo.getAgencyId());
	        	  this.mAppInfoCache.remove(appinfo.getId());
	        	  this.mAppInfoCache.put(appinfo.getId(), localApp);        	 
	          }
	         
	          //localApp = (AppInfo)this.mAppInfoCache.get(appinfo.getId());
	          localApp.updateAppStatus();
	          /*if(localApp.mBaseListeners!= null)
	          {Log.v("containsKey containsKey 2");
	        	  Iterator<AppInfoUpdateListener> localIterator2 = localApp.mBaseListeners.iterator();
		            while (localIterator2.hasNext())
		            {
		            	Log.v("containsKey containsKey 3");
		              AppInfo.AppInfoUpdateListener listener = (AppInfo.AppInfoUpdateListener)localIterator2.next();
		              //listener.onContentUpdate(localApp);
		              //listener.onStatusUpdate(localApp);
		            }  
	          }	      
	          Log.v("localApp:"+ localApp.getName());
	          */
	          return localApp;
	        }  
	      }
	        	     
	     return appinfo;
	    }

	  
	    public AppInfo get(String appid)
	    {
	      AppInfo appInfo;
	      if (TextUtils.isEmpty(appid))
	        appInfo = null;
	      else
	      {	       
	        appInfo = (AppInfo)this.mAppInfoCache.get(appid);	        
	      }
	    
	      return appInfo;
	   }
	    
	   
	    public AppInfo getAppByPackage(String pakagename)
	    {
	    	Iterator<String> iterator = null;
	    	if ((this.mPackageToAppIdMap != null) && (!this.mPackageToAppIdMap.isEmpty()))
	    	{
	    		CopyOnWriteArraySet<String> localCopyOnWriteArraySet = (CopyOnWriteArraySet<String>)this.mPackageToAppIdMap.get(pakagename);
	    		if ((localCopyOnWriteArraySet != null) && (!localCopyOnWriteArraySet.isEmpty()))
	  	          iterator = localCopyOnWriteArraySet.iterator();
		    	  while (iterator.hasNext())
		  	      {		    		
		  	        AppInfo appinfo = get((String)iterator.next());
		  	        if (appinfo != null)
		  	        {
		  	          return appinfo;
		  	        }
		    	 }
	    	}
			return null;
	  } 
	    
	}
}
